#include <memory>
#include "acc_driver.h"
#include "connect.h"

using namespace std;
using namespace lc;
using namespace su;
using namespace proto;


void proto::ConToAcc::VerifyReq(uint64 cid, uint32 cmd, const std::string& msg)
{
	ConToAcc* con = dynamic_cast<ConToAcc*>(RpcNetClient::GetCurCon());
	L_COND_V(con);	//稳定后可删
	L_COND_V(cid);	  //稳定后可删
	ADFacadeMgr::Ins().m_iADFacade->OnRevVerifyReq(SessionId{cid, con->m_accId }, cmd, msg);
}

void proto::ConToAcc::Forward(uint64 cid, uint32 cmd, const string& msg)
{
	ConToAcc* con = dynamic_cast<ConToAcc*>(RpcNetClient::GetCurCon());
	L_COND_V(con);	  //稳定后可删
	L_COND_V(cid);	  //稳定后可删

	auto it = con->m_id2sn.find(cid);
	if (it == con->m_id2sn.end())
	{
		L_ERROR("can't find session when rev ntf discon. %lld %d", cid, con->AccId());
		return;
	}

	ADFacadeMgr::Ins().m_iADFacade->OnRevClientMsg(it->second, cmd, msg);
}

void proto::ConToAcc::NtfDiscon(uint64 cid)
{
	//LDEBUG("NtfDiscon cid=", cid);
	ConToAcc* con = dynamic_cast<ConToAcc*>(RpcNetClient::GetCurCon());
	L_COND_V(con);	  //稳定后可删
	L_COND_V(cid);	  //稳定后可删

	auto it = con->m_id2sn.find(cid);
	if (it == con->m_id2sn.end())
	{
		L_DEBUG("can't find session when rev ntf discon. %lld %d", cid, con->m_accId);
		return;
	}
	ADFacadeMgr::Ins().m_iADFacade->OnClientDisCon(it->second);
	con->m_id2sn.erase(it);
}

void proto::ConToAcc::OnDisconnected()
{
	for (auto& v : m_id2sn)
	{
		const Session& sn = v.second;
		ADFacadeMgr::Ins().m_iADFacade->OnClientDisCon(sn);
	}
	m_id2sn.clear();
	ADFacadeMgr::Ins().m_iADFacade->OnAccDisCon(GetRemoteIp(), GetRemotePort());
	RpcNetClient::OnDisconnected();
}

proto::ConToAcc::ConToAcc(uint32 accId)
	:m_accId(accId)
{
	RegFun(ConToAcc::Forward, (char)AdCmd::Forward);
	RegFun(ConToAcc::VerifyReq, (char)AdCmd::VerifyReq);
	RegFun(ConToAcc::NtfDiscon, (char)AdCmd::NtfDiscon);
}

void proto::ConToAcc::OnConInitOk()
{
	auto f = [this](bool isOk) //this安全， 是本对象调用
	{
		L_COND_V(!m_isReg, "repeated res reg");

		if (!isOk)
		{
			L_FATAL("reg error, stop connect");
			DisConnect();
			return;
		}
		m_isReg = true;
		ADFacadeMgr::Ins().m_iADFacade->OnRegOK(m_accId);
	};
		
	Call(f, (char)AccCmd::Reg, ConMgr::Ins().GetMainCmd(), ConMgr::Ins().GetSvrId(), ConMgr::Ins().IsVerify());

	if (const AccSeting* req = ConMgr::Ins().GetMsgAccSeting())
	{
		L_DEBUG("OnConnected. send MsgAccSeting info. %d %d %d no_msg_interval_sec=%d, m_acc_id=%d",
			req->hbi.req_cmd, req->hbi.rsp_cmd, req->hbi.interval_sec,
			req->noMsgSpanSec, m_accId);
		Call((char)AccCmd::Seting, *req);
	}
}

const proto::Session* proto::ConToAcc::FindSession(const SessionId& id)
{
	if (id.accId != m_accId)
	{
		return nullptr;
	}
	auto it = m_id2sn.find(id.cid);
	if (it == m_id2sn.end())
	{
		return nullptr;
	}
	return &(it->second);
}

const proto::Session* proto::ConToAcc::FindSession(uint64 cid)
{
	auto it = m_id2sn.find(cid);
	if (it == m_id2sn.end())
	{
		return nullptr;
	}
	return &(it->second);
}

bool proto::SessionId::operator<(const SessionId &a) const
{
	if (cid == a.cid)
	{
		return accId < a.accId;
	}
	return cid < a.cid;
}

bool proto::ConMgr::Init(const AdFacadeInitData& data)
{
	L_COND_F(m_vecCon.empty(), "repeated init");
	L_COND_F(!data.vecAccAddr.empty());
	L_COND_F(data.mainCmd != 0);
	L_COND_F(data.svrId != 0);
	{//check repeated
		std::set<AccAddr> s;
		for (auto &v : data.vecAccAddr)
		{
			if (!s.insert(v).second)
			{
				L_ERROR("repeated addr %s %d", v.ip.c_str(), v.port);
				return false;
			}
		}
	}
	m_svrId = data.svrId;
	m_mainCmd = data.mainCmd;
	m_isVerifySvr = data.isVerifySvr;
	for (auto &v : data.vecAccAddr)
	{
		if (!AddAcc(v.ip.c_str(), v.port))
		{
			return false;
		}
	}
//	L_DEBUG("m_vec_con or acc size=%d", m_vec_con.size());
	return true;

}

bool proto::ConMgr::AddAcc(const AccAddr &addr)
{
	L_COND_F(0 != m_svrId, "must call  init before call AddAcc");
	for (std::unique_ptr<ConToAcc> &p : m_vecCon)
	{
		if (addr.ip == p->GetRemoteIp() && addr.port == p->GetRemotePort())
		{
			L_ERROR("repeated connect addr");
			return false;
		}
	}

	return AddAcc(addr.ip.c_str(), addr.port);
}

proto::ConToAcc* proto::ConMgr::FindConToAcc(const SessionId& id) const
{
	//L_DEBUG("m_vec_con.size()=%d", m_vec_con.size());
	if (id.accId == (uint32)-1)
	{
		return nullptr;
	}	
	L_COND(id.accId < m_vecCon.size(), nullptr, "accId overload %d", id.accId);
	auto p = m_vecCon[id.accId].get();
	return p;
}

const proto::Session* proto::ConMgr::FindSession(const SessionId& id) const
{
	if (id.cid == 0)
	{
		return nullptr;
	}
	ConToAcc* con = FindConToAcc(id);
	if (nullptr == con)
	{
		return nullptr;
	}
	if (!con->IsReg())
	{
		return nullptr;
	}
	return con->FindSession(id);
}

void proto::ConMgr::SetAccSeting(const AccSeting &seting)
{
	if (!m_seting)
	{
		m_seting = std::make_unique<AccSeting>();
	}
	*m_seting = seting;
	for (auto &p : m_vecCon)
	{
		if (!p->IsReg())
		{
			continue;
		}
		p->Call((char)AccCmd::Seting, seting);
	}
}

uint32 proto::ConMgr::SessionNum()
{
	uint32 num = 0;
	for (auto& i : m_vecCon)
	{
		num+=i->SessionNum();
	}
	return num;
}

bool proto::ConMgr::AddAcc(const char* ip, uint16 port)
{
	unique_ptr<ConToAcc> p = make_unique<ConToAcc>(m_vecCon.size());
	auto OnConInitOk = std::bind(&ConToAcc::OnConInitOk, p.get());//回调由 p对象方法调用， 所能能保证p不野
	if (!p->Init(ip, port, OnConInitOk))
	{
		L_ERROR("connect acc fail. ip port= %s, %d", ip, port);
		return false;
	}
	m_vecCon.push_back(std::move(p));
	return true;
}

bool proto::AccAddr::operator<(const AccAddr &other) const
{
	if (port == other.port)
	{
		return ip < other.ip;
	}
	return port < other.port;
}

